/**
 * @fileOverview The core bundle of method extensions to Function.prototype
 * @author <a href="mailto:david@ondevice.ie">David Foley</a>
 * @version 0.3
 */
new function ()
{
        /**
         * @private
         * A static reference to Function.prototype
         * @type {Object}
         */
    var F= Function.prototype

        /**
         * @private
         * A static reference to the word anonymous
         * @type {String}
         */
    ,   A= 'anonymous'
        /**
         * @private
         * A static reference to the Array.prototype.slice method
         * @type {Function}
         */
    ,   slice= Array.prototype.slice

        /**
         * @private
         * Flag used to prevent or allow member overriding
         * @type {Boolean}
         */
    ,   preventOverride= false

        /**
         * @private
         * Internal method used by traits.implementStatic to implement traits.
         * 
         * TODO Function.displayName integration for debugging in FF and WebKit
         *
         * @param {String} accessor        The name of the trait
         * @param {Object} value           The value of the trait
         * @param {Object} subject         The object on which the trait will be put
         * @param {Boolean} canOverwrite   Whether to allow overwriting or not
         * @this {Function}
         * @return {void}
         */
    ,   put= function (accessor, value, subject, canOverwrite)
        {
            if (canOverwrite || !(accessor in subject)) 
            {
                if (value instanceof Function) 
                {
                    if (!value.displayName) 
                    {
                        value.displayName= '#' + accessor;
                    }
                    subject[accessor]= value;
                }
            }
                
        }

        /*
         * The traits that will be implemented on Function.prototype.
         */
    ,   traits=
        {
            /**
             * Extend one constructors prototype by anothers. 
             * 
             * @example Child.inherit(Parent);
             * 
             * @example
             * <code><pre>
             * // a mock superclass
             * Parent.implement
             * (
             *     function setValue (value)
             *     {
             *         this.value= value;
             *     }
             * );
             * 
             * function Parent () {;};
             * 
             * // a mock subclass
             * Child.inherit(Parent).implement
             * (
             *     // override the parent method
             *     function setValue (value)
             *     {
             *         // introduce a guard clause
             *         if (false === Number.is(value))
             *             throw new TypeError ('Number expected');
             *             
             *         // call the superclass method like this
             *         this.parent.setValue.call(this, value);
             *     }
             * 
             * );
             * 
             * function Child () 
             * {
             *     // apply the superclass constructor logic to this instance
             *     Parent.call(this, 'defaultChildValue');
             * }
             * 
             * var child= new Child;
             * alert(Parent.is(child)); // true
             * alert(child.parent === Parent.prototype); //true
             * </pre></code>
             * 
             * @name inherit
             * @methodOf Function
             * @this {Function}
             * @param {Function} Parent    The constructor to inherit from.
             * @return {Function}          The extended prototypes constructor.
             * @throws {TypeError}         If Parent is an invalid constructor.
             */
            inherit: function (Parent)
            {
                if ('function' !== typeof Parent || this === Parent)
                    throw new TypeError('Inheritance error.');

                function Intermediary () {;};
                Intermediary.prototype= Parent.prototype;

                this.prototype= new Intermediary;
                this.prototype.constructor= this;
                this.prototype.parent= Parent.prototype;
                return this;
            }
            
            /**
             * Implement any number of traits on a constructors prototype. Traits
             * are added subject to the same rules enforced by #implementStatic.
             * 
             * @example
             * <code><pre>
             * // 1. Using named functions
             * CustomObject.implement
             * (
             *     function method () 
             *     {
             *         // implementation
             *     }
             * );
             * 
             * function CustomObject ()
             * {
             *     this.method();
             * };
             * </pre></code>
             * 
             * @description description continued here
             * @example
             * <code>
             * <pre>
             * //2. Using an object literal
             * CustomObject.implement
             * (
             *     {
             *         property: null
             *         
             *     ,   method: function () 
             *         {
             *             // implementation
             *         }
             *     }
             * );
             * 
             * </pre>
             * </code>
             * The method also lends itself to trait borrowing quite well.
             * 
             * @example
             * <code><pre>
             * AnotherCustomObject.implement(CustomObject.prototype);
             * function AnotherCustomObject ()
             * {
             *     this.method();
             * };
             * </pre></code>
             * 
             * <p>And defining dynamic class instances</p>
             * @example
             * <code><pre>
             * var mixin= new function (value)
             * {
             *     var hiddenValue= value;
             *     this.constructor.implement
             *     (
             *         function getHiddenValue () 
             *         {
             *             return hiddenValue
             *         }
             *     );
             * }('someValue');
             * </pre></code>
             * 
             * @name implement
             * @methodOf Function
             * @this {Function}
             * 
             * @param {Object|Function...} arguments
             * @return {Function} The constructor 
             * @see #implementStatic
             */
        ,   implement: function ()
            {
                // there is the potential for this constructors prototypes
                // constructor being overwritten during the course of implementing
                // traits, so we make sure to apply the correct value here at all
                // times, simplifying the trait implementation process
               return this.implementStatic.apply(this.prototype, arguments).constructor= this;
            }
            
            /**
             * Implement any number of traits on a constructor.
             * 
             * @name implementStatic
             * @methodOf Function
             * @this {Function}
             * @param {Object|Function...} arguments
             * @return {Function}
             * @see #implement
             */
        ,   implementStatic: function ()
            {
                var trait= null
                ,   canOverwrite= !F.implement.preventOverride
                ,   accessor
                ,   n= arguments.length
                ,   i= 0
                ;
                
                for (; i < n; i++)
                {
                    trait= arguments[i];
                    if (null != trait) 
                    {
                        if ('function' === typeof trait) 
                        {
                            if (A !== (accessor= trait.getName()))
                                put (accessor, trait, this, canOverwrite);
                        }
                        else for (accessor in trait)
                        {
                            if (trait.hasOwnProperty(accessor))
                                put (accessor, trait[accessor], this, canOverwrite)
                        }
                    } 
                };
                
                // reset override protection defaults
                F.implement.preventOverride= preventOverride;

                return this;
            }
            
            /**
             * Determine if a value is an instance of a type, eliding the 
             * distinction between primitives and their object counterparts.
             * 
             * The method will always return false for values of null or
             * undefined.
             * 
             * @name is
             * @methodOf Function
             * @this {Function}
             * @param {Object} value
             * @return {Boolean}
             */
        ,   is: function (value)
            {
                //return null == value ? false : Object(value) instanceof this;
                
                if (null == value)
                    return false;
                
                // cast primitive strings, booleans and numbers to Object    
                if (false === value instanceof Object)
                    value= Object(value);
                    
                return value instanceof this;
            }
            
            /**
             * Set a functions execution scope and optionally preset the
             * arguments that will be supplied to it on execution.
             * 
             * @name bind
             * @methodOf Function
             * @this {Function}
             * 
             * @param {Object} executionScope    The functions execution scope
             * @param {Object...} presetArgs     The arguments the function will use
             * @return {Function}                A curried function
             */
        ,   bind: function (executionScope)
            {
                var method= this
                ,   presetArgs
                ;

                if (2 > arguments.length) return function ()
                {
                     return method.apply(executionScope, arguments);
                }

                presetArgs= slice.call(arguments, 1);
                return function ()
                {
                    return method.apply(executionScope, presetArgs.concat(arguments));
                };
            }
            
            /**
             * Determine a functions name. If a function is anonymous, or otherwise
             * does not have a name, the return value will be 'anonymous'.
             * 
             * @name getName
             * @methodOf Function
             * @this {Function}
             * 
             * @return {String} The name of a function.
             */
        ,   getName: new function () 
            {
                if ('name' in F) return function () 
                {
                    return this.name || A;
                };
                
                return function () 
                {
                    var name= this.name;
                    if (name)  // if the functions name has been cached
                        return name; // return it
                    
                    // else decompile and parse the function to get its name    
                    name= String(this).match(/^function\s([\w|$]+)/);
                    // then cache it before returning
            	    return this.name= String(name ? name[1] : A);
                };
            }
        }
    ;
    
    // A metaprogramming approach to set up the object model in three steps:

    // 1. enable override protection, so as not to override any existing methods
    traits.implement.preventOverride= true;

    // 2. copy over the implement method to Function.prototype
    F.implement= traits.implement;

    // 3. call implementStatic in the execution context of Function.prototype
    traits.implementStatic.call(F, traits);
};